Skip to content

feat: EXPIRETIME, PEXPIRETIME, SMOVE, and SINTERCARD - #315

Merged
kacy merged 1 commit into
mainfrom
feat/expiretime-smove-sintercard
Feb 26, 2026
Merged

feat: EXPIRETIME, PEXPIRETIME, SMOVE, and SINTERCARD#315
kacy merged 1 commit into
mainfrom
feat/expiretime-smove-sintercard

Conversation

@kacy

@kacy kacy commented Feb 26, 2026

Copy link
Copy Markdown
Owner

summary

adds four Redis-compatible commands that cover common expiry-inspection and
set-manipulation patterns:

  • EXPIRETIME / PEXPIRETIME — return the absolute Unix timestamp when a
    key will expire (seconds and milliseconds respectively). internally the store
    uses a monotonic clock, so a new monotonic_to_unix_ms helper in time.rs
    anchors it to wall-clock time on first call and uses arithmetic only after that.
  • SMOVE src dst member — atomically moves a member between sets. when
    source and destination land on the same shard the operation is a single
    keyspace call; when they differ, execute.rs does a SRem then SAdd across
    the two shards.
  • SINTERCARD numkeys key... [LIMIT n] — returns the intersection cardinality
    with an optional upper bound. each key is fetched from its owning shard via
    SMembers, and the intersection is computed in the execute layer so cross-shard
    key sets work correctly.

AOF persistence follows the same pattern as the rest of the codebase: SMOVE on a
single shard records two entries (SRem + SAdd); for cross-shard SMOVE, each
sub-request persists to its own shard's log. EXPIRETIME and PEXPIRETIME are
read-only and produce no AOF records.

what was tested

  • cargo test --test integration — 135 tests pass, including 12 new tests:
    • expiretime_returns_absolute_epoch, expiretime_no_expiry_returns_minus_one,
      expiretime_missing_key_returns_minus_two
    • pexpiretime_precision, pexpiretime_no_expiry_returns_minus_one,
      pexpiretime_missing_key_returns_minus_two
    • smove_basic, smove_missing_member_returns_zero, smove_missing_source_returns_zero
    • sintercard_basic, sintercard_with_limit, sintercard_missing_key_returns_zero
  • cargo clippy -p ember-protocol -p emberkv-core -p ember-server -- -D warnings — clean
  • unit tests added to keyspace/set.rs for smove and sintercard edge cases

design notes

the cross-shard SMOVE is non-atomic in the distributed sense — a crash between
the SRem and SAdd steps would leave the member only in the source set. this is
the same trade-off made by RENAME and COPY for cross-shard keys and is acceptable
for a single-node store. if strong atomicity across shards is ever needed, the
right path is a two-phase write log, not adding complexity now.

adds four new Redis-compatible commands:

- EXPIRETIME key — returns absolute Unix timestamp (seconds) when the key
  expires; -1 if no expiry, -2 if the key doesn't exist
- PEXPIRETIME key — same but in milliseconds
- SMOVE src dst member — atomically moves a member between sets; handles
  cross-shard keys by doing a srem + sadd across shards
- SINTERCARD numkeys key... [LIMIT n] — returns the cardinality of the
  intersection of N sets with an optional cap; fetches members from each
  key's owning shard before computing the intersection in the execute layer

the internal expiry clock is monotonic (ms since process start), so a new
monotonic_to_unix_ms() helper in time.rs anchors it to wall-clock time once
and uses fast arithmetic on every subsequent call.

AOF persistence: SMOVE on a single shard writes two records (SRem + SAdd)
which replay cleanly; cross-shard SMOVE persists each sub-command to its
own shard's AOF log.

12 integration tests, unit tests for smove and sintercard in keyspace/set.rs.
@kacy
kacy merged commit c788d9e into main Feb 26, 2026
@kacy
kacy deleted the feat/expiretime-smove-sintercard branch February 26, 2026 19:51
kacy added a commit that referenced this pull request Feb 27, 2026
adds command metadata for all commands from prs #315–319:
- generic: expireat, expiretime, pexpireat, pexpiretime
- string: getset, msetnx
- bitmap (new group): bitcount, bitop, bitpos, getbit, setbit
- list: lmpop; update lpop/rpop args to include optional count
- hash: hrandfield
- set: sintercard, smove
- sorted_set: zmpop, zrandmember

all entries are alphabetically ordered within their group.
kacy added a commit that referenced this pull request Feb 27, 2026
…320)

* feat(cli): surface 17 new commands in autocomplete and help

adds command metadata for all commands from prs #315–319:
- generic: expireat, expiretime, pexpireat, pexpiretime
- string: getset, msetnx
- bitmap (new group): bitcount, bitop, bitpos, getbit, setbit
- list: lmpop; update lpop/rpop args to include optional count
- hash: hrandfield
- set: sintercard, smove
- sorted_set: zmpop, zrandmember

all entries are alphabetically ordered within their group.

* feat(grpc): add 17 new rpcs — bitmap, expireat, getset, smove, lmpop, zmpop, hrandfield, zrandmember

* feat(clients): add 17 new commands — bitmap, expireat, getset, smove, lmpop, zmpop, hrandfield, zrandmember
kacy added a commit that referenced this pull request Feb 27, 2026
marks GETSET, GETDEL, GETEX, MSETNX, WAIT, EXPIREAT, PEXPIREAT,
EXPIRETIME, PEXPIRETIME, LMOVE, LMPOP, HRANDFIELD, SMOVE, SINTERCARD,
ZUNION, ZINTER, ZDIFF, ZRANDMEMBER, ZMPOP as implemented in the
compatibility guide. adds a new bitmap section for BITCOUNT, GETBIT,
SETBIT, BITOP, BITPOS.

ember-client readme gains new rows for all 17 commands from prs
#315-319, a bitmaps section, and correct expiry/random-field signatures.

also fixes a clippy error (approximate pi literal in a unit test) and
two assert_eq!(bool) warnings in the client crate.
kacy added a commit that referenced this pull request Feb 27, 2026
the go client was failing to compile because pb/ stubs were stale —
missing the 17 rpcs added in prs #315–319 (expiretime, pexpiretime,
expireat, pexpireat, getset, msetnx, getbit, setbit, bitcount, bitpos,
bitop, smove, sintercard, lmpop, zmpop, hrandfield, zrandmember).

regenerated with `make proto-gen` from clients/ember-go/.
kacy added a commit that referenced this pull request Feb 27, 2026
the go client was failing to compile because pb/ stubs were stale —
missing the 17 rpcs added in prs #315–319 (expiretime, pexpiretime,
expireat, pexpireat, getset, msetnx, getbit, setbit, bitcount, bitpos,
bitop, smove, sintercard, lmpop, zmpop, hrandfield, zrandmember).

regenerated with `make proto-gen` from clients/ember-go/.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant